#ifndef __M_QUEUE_H__
#define __M_QUEUE_H__
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <google/protobuf/map.h>

namespace zymq
{
    struct MsgQueue
    {
        using ptr = std::shared_ptr<MsgQueue>;
        std::string name;
        bool durable;
        bool exclusive;
        bool auto_delete;
        google::protobuf::Map<std::string, std::string> args;

        MsgQueue() {}
        MsgQueue(const std::string &qname,
                 bool qdurable,
                 bool qexclusive,
                 bool qauto_delete,
                 const google::protobuf::Map<std::string, std::string> &qargs)
            : name(qname), durable(qdurable), exclusive(qexclusive),
              auto_delete(qauto_delete), args(qargs) {}

        void setArgs(const std::string &str_args)
        {
            std::vector<std::string> sub_args;
            StrHelper::split(str_args, "&", sub_args);
            for (auto &str : sub_args)
            {
                std::vector<std::string> kv;
                StrHelper::split(str, "=", kv);
                args[kv[0]] = kv[1];
            }
        }

        std::string getArgs()
        {
            std::string ret;
            for (auto &kv : args)
            {
                ret += kv.first;
                ret += "=";
                ret += kv.second;
                ret += "&";
            }
            return ret;
        }
    };

    using QueueMap = std::unordered_map<std::string, MsgQueue::ptr>;

    class MsgQueueMapper
    {
    public:
        MsgQueueMapper(const std::string & dbfile) :_sql_helper(dbfile)
        {
            std::string ppath = FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(ppath);
            assert(_sql_helper.open());

            createTable();
        }
        void createTable()
        {
            #define CREATE_QUEUE_TABLE "create table if not exists queue_table(\
                name varchar(32) primary key,\
                durable int,\
                exclusive int,\
                auto_delete int,\
                args varchar(128));"

            if(_sql_helper.exec(CREATE_QUEUE_TABLE, nullptr, nullptr) == false)
            {
                ELOG("创建队列数据库表失败!!");
                abort();
            }
        }
        void removeTable()
        {
            #define DROP_QUEUE_TABLE "drop table if exists queue_table;"

            if(_sql_helper.exec(DROP_QUEUE_TABLE, nullptr, nullptr) == false)
            {
                ELOG("删除队列数据库表失败!!");
            }
        }
        bool insert(MsgQueue::ptr & mq)
        {
            #define INSERT_QUEUE_SQL "insert into queue_table values('%s', %d, %d, %d, '%s');"
            
            char sql_str[4096] = {0};
            std::string args_str = mq->getArgs();
            sprintf(sql_str, INSERT_QUEUE_SQL, mq->name.c_str(), mq->durable, mq->exclusive, mq->auto_delete, args_str.c_str());

            if(_sql_helper.exec(sql_str, nullptr, nullptr) == false)
            {
                ELOG("新增数据失败!!");
                return false;
            }
            return true;
        }
        void remove(const std::string &name)
        {
            std::stringstream ss;
            ss << "delete from queue_table where name=";
            ss << "'" << name << "';";

            if(_sql_helper.exec(ss.str(), nullptr, nullptr) == false)
            {
                ELOG("删除数据失败!!");
                return;
            }
        }
        QueueMap recovery()
        {
            QueueMap ret;
            #define SELECT_QUEUE_SQL "select name, durable, exclusive, auto_delete, args from queue_table;"

            _sql_helper.exec(SELECT_QUEUE_SQL, selectCallback, &ret);

            return ret;
        }
    private:
        static int selectCallback(void *arg, int col_count, char **result, char **fields_name)
        {
            QueueMap* pret = (QueueMap*)arg;

            MsgQueue::ptr mqp = std::make_shared<MsgQueue>();
            mqp->name = result[0];
            mqp->durable = std::stoi(result[1]);
            mqp->exclusive = std::stoi(result[2]);
            mqp->auto_delete = std::stoi(result[3]);
            if(result[4]) mqp->setArgs(result[4]);

            pret->insert({result[0], mqp});

            return 0;
        }
    private:
        SqliteHelper _sql_helper;
    };
    class MsgQueueManager 
    {
    public:
        using ptr = std::shared_ptr<MsgQueueManager>;

        MsgQueueManager(const std::string &dbfile):_mapper(dbfile) 
        {
            _msg_queues = _mapper.recovery();
        }
        bool declareQueue(const std::string &name,
                             bool durable, bool exclusive, bool auto_delete,
                             const google::protobuf::Map<std::string, std::string> &args)
        {
            // 先锁起来，再查询
            std::unique_lock<std::mutex> lock(_mutex);
            if(_msg_queues.find(name) == _msg_queues.end())
            {
                MsgQueue::ptr mqp = std::make_shared<MsgQueue>(name, durable, exclusive, auto_delete, args);
                if(durable == true)
                {
                    bool ret = _mapper.insert(mqp);
                    if(ret == false) return false;
                }
                _msg_queues.insert(std::make_pair(name, mqp));
                return true;
            }
            return true;
        }
        void deleteQueue(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            // 交换机不存在，直接退出
            if(it == _msg_queues.end()) return;

            // 是持久存储，才从数据库中删除
            if(it->second->durable == true) _mapper.remove(name);
            _msg_queues.erase(name);
        }
        MsgQueue::ptr selectQueue(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            // 交换机不存在，直接退出
            if(it == _msg_queues.end()) return nullptr;

            return it->second;
        }
        QueueMap allQueues()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _msg_queues;
        }
        bool exists(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            // 交换机不存在，直接退出
            if(it == _msg_queues.end()) return false;

            return true;
        }
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _msg_queues.size();
        }
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _msg_queues.clear();
        }
    private:
        std::mutex _mutex;
        MsgQueueMapper _mapper;
        QueueMap _msg_queues;
    };
}

#endif